package collector

import (
	"fmt"

	"libvirt.org/go/libvirt"
)

// CollectionError represents an error that occurred during metric collection
type CollectionError struct {
	Domain  string
	Metric  string
	Op      string
	Err     error
	Context map[string]any
}

// Error implements the error interface
func (e *CollectionError) Error() string {
	contextStr := ""
	if len(e.Context) > 0 {
		contextStr = " with context: " + fmt.Sprintf("%v", e.Context)
	}
	return fmt.Sprintf("failed to collect %s metric for domain %s (operation: %s)%s: %v",
		e.Metric, e.Domain, e.Op, contextStr, e.Err)
}

// Unwrap returns the wrapped error
func (e *CollectionError) Unwrap() error {
	return e.Err
}

// NewCollectionError creates a new collection error
func NewCollectionError(domain, metric, op string, err error, context map[string]any) *CollectionError {
	if context == nil {
		context = make(map[string]any)
	}
	return &CollectionError{
		Domain:  domain,
		Metric:  metric,
		Op:      op,
		Err:     err,
		Context: context,
	}
}

// IsLibvirtError checks if the error is a libvirt error with specific code
func IsLibvirtError(err error, code libvirt.ErrorNumber) bool {
	if err == nil {
		return false
	}

	// Check if it's a CollectionError and unwrap
	if collErr, ok := err.(*CollectionError); ok {
		err = collErr.Err
	}

	// Check if it's a libvirt.Error
	if lverr, ok := err.(libvirt.Error); ok {
		return lverr.Code == code
	}

	return false
}

// IsDomainNotRunning checks if the error indicates domain is not running
func IsDomainNotRunning(err error) bool {
	return IsLibvirtError(err, libvirt.ERR_OPERATION_INVALID)
}

// IsDomainNotFound checks if the error indicates domain was not found
func IsDomainNotFound(err error) bool {
	return IsLibvirtError(err, libvirt.ERR_NO_DOMAIN)
}

// SafeGetDomainName safely gets domain name with error handling
func SafeGetDomainName(domain *libvirt.Domain) string {
	if domain == nil {
		return "unknown"
	}
	name, err := domain.GetName()
	if err != nil {
		return "unknown"
	}
	return name
}

// SafeGetDomainUUID safely gets domain UUID with error handling
func SafeGetDomainUUID(domain *libvirt.Domain) string {
	if domain == nil {
		return "unknown"
	}
	uuid, err := domain.GetUUIDString()
	if err != nil {
		return "unknown"
	}
	return uuid
}
